# -*- coding: utf-8 -*-
"""
TG表格组件 - TgTable
表格组件，用于显示表格数据
"""
from PySide6.QtWidgets import (QWidget, QTableWidget, QTableWidgetItem, 
                               QHeaderView, QVBoxLayout, QStyledItemDelegate, QStyle, QStyleOptionViewItem)
from PySide6.QtCore import Qt, Signal, QTimer
from PySide6.QtGui import QColor, QBrush, QPalette
from ...style_manager import StyleManager
import pandas as pd
from typing import Union, Callable, Optional, Dict, List
import re

# 自定义角色，用于在项中存储颜色信息
BackgroundColorRole = Qt.UserRole + 1
TextColorRole = Qt.UserRole + 2
NumericValueRole = Qt.UserRole + 3  # 存储数值用于排序

class TgTableWidgetItem(QTableWidgetItem):
    """支持数值排序的表格项"""
    
    def __init__(self, text="", numeric_columns=None, column_index=None):
        super().__init__(text)
        self.numeric_columns = numeric_columns or set()
        self.column_index = column_index
        
        # 如果是数值列，尝试解析并存储数值
        if column_index is not None and column_index in self.numeric_columns:
            self._store_numeric_value(text)
    
    def _store_numeric_value(self, text):
        """解析并存储数值，用于排序"""
        try:
            # 移除百分号、逗号、空格等，保留正负号
            sanitized = str(text).strip()
            if sanitized in ['', 'N/A', '-', '--']:
                numeric_value = 0.0
            else:
                # 移除百分号和千分位分隔符
                sanitized = sanitized.replace('%', '').replace(',', '').replace(' ', '')
                numeric_value = float(sanitized)
            
            self.setData(NumericValueRole, numeric_value)
        except (ValueError, TypeError):
            # 如果无法解析，设为0
            self.setData(NumericValueRole, 0.0)
    
    def __lt__(self, other):
        """自定义小于比较，用于排序"""
        # 避免调用基类 __lt__，在某些绑定版本中会导致递归
        if isinstance(other, QTableWidgetItem):
            # 如果两个项都有数值数据，按数值比较
            self_numeric = self.data(NumericValueRole)
            other_numeric = other.data(NumericValueRole)

            if isinstance(self_numeric, (int, float)) and isinstance(other_numeric, (int, float)):
                return self_numeric < other_numeric

            # 否则按字符串比较
            return str(self.text()) < str(other.text())

        # 非同类项，保守按文本比较
        other_text = getattr(other, 'text', lambda: '')()
        return str(self.text()) < str(other_text)

class TgTableItemDelegate(QStyledItemDelegate):
    """自定义项委托，用于处理单元格的自定义背景和文本颜色"""
    
    def paint(self, painter, option, index):
        """
        重写绘制方法，以编程方式应用自定义颜色，绕过样式表冲突。
        该实现通过先绘制自定义背景，然后调用父类的paint方法（但禁用其背景绘制）来绘制前景，
        从而避免手动重绘文本带来的问题。
        """
        opt = QStyleOptionViewItem(option)
        self.initStyleOption(opt, index)

        bg_color = index.data(BackgroundColorRole)
        text_color = index.data(TextColorRole)
        is_selected = opt.state & QStyle.State_Selected

        # 1. 如果有自定义背景色，并且单元格未被选中，我们先绘制它
        if bg_color and not is_selected:
            painter.fillRect(opt.rect, bg_color)

        # 2. 修改选项中的调色板以应用自定义文本颜色
        if text_color:
            opt.palette.setColor(QPalette.ColorRole.Text, text_color)
            if is_selected:
                # 即使单元格被选中，我们也强制使用自定义的文本颜色
                opt.palette.setColor(QPalette.ColorRole.HighlightedText, text_color)

        # 3. 调用父类的paint方法来绘制前景（文本、边框、选中态等）
        # 我们通过将背景刷设置为NoBrush来暂时禁用父类的背景绘制，防止它覆盖我们的自定义背景
        original_brush = opt.backgroundBrush
        if bg_color and not is_selected:
            opt.backgroundBrush = QBrush(Qt.NoBrush)

        super().paint(painter, opt, index)

        # 恢复原始的背景刷，以避免影响其他单元格的绘制
        if bg_color and not is_selected:
            opt.backgroundBrush = original_brush

class TgTableColumnStyle:
    """表格列颜色风格管理类"""
    
    # 预定义颜色风格
    PROFIT_LOSS = 'profit_loss'  # 盈亏风格：负数绿色，正数红色
    HEATMAP = 'heatmap'          # 热力图：根据数值在颜色方案中渐变
    PERFORMANCE = 'performance'  # 性能风格：负数红色，正数绿色
    GRADIENT_RAINBOW = 'gradient_rainbow'  # 彩虹渐变：从小到大彩虹色
    
    # 预定义颜色方案
    COLOR_SCHEMES = {
        PROFIT_LOSS: {'positive': '#C1272D', 'negative': '#008B45'},  # 红涨绿跌
        PERFORMANCE: {'positive': '#008B45', 'negative': '#C1272D'},  # 绿好红坏
        HEATMAP: {'min': '#E8F5E9', 'max': '#1B5E20'}, # 默认热力图（浅绿到深绿）
    }
    
    def __init__(self, style_type: str, column_identifier: Union[int, str], 
                 background_color: bool = True, text_color: bool = False):
        """
        初始化列颜色风格
        
        Args:
            style_type: 风格类型，使用预定义常量
            column_identifier: 列标识符，可以是列索引(int)或列名(str)
            background_color: 是否应用背景色
            text_color: 是否应用文字色
        """
        self.style_type = style_type
        self.column_identifier = column_identifier
        self.background_color = background_color
        self.text_color = text_color
        
        # 内置颜色配置和范围将在运行时动态设置
        self.custom_colors = None
        self.value_range = None
    
    def set_colors_and_range(self, custom_colors, value_range):
        """设置颜色配置和数值范围（内部使用）"""
        self.custom_colors = custom_colors or {}
        self.value_range = value_range
        
        # 如果是热力图且未提供自定义颜色，则使用默认方案
        if self.style_type.startswith(self.HEATMAP) and not self.custom_colors:
            self.custom_colors = self.COLOR_SCHEMES[self.HEATMAP]
    
    def get_style_for_value(self, value: Union[str, int, float]) -> tuple:
        """
        根据值获取样式
        
        Args:
            value: 单元格值
            
        Returns:
            tuple: (background_color, text_color) 或 (None, None)
        """
        # === 第一步：数值预处理 ===
        # 尝试将输入值转换为数值，支持多种格式
        # 兼容百分比（如"12.34%"）、千分位（如"1,234.5"）等格式
        # 重要：保留正负号，确保涨跌判断正确
        sanitized_value = value
        if isinstance(value, str):
            # 去除首尾空格，但保留正负号
            sanitized_value = value.strip()
            # 移除百分号和千分位分隔符，但保留正负号
            if sanitized_value.startswith(('+', '-')):
                # 如果有明确的正负号，保留它
                sign = sanitized_value[0]
                number_part = sanitized_value[1:].replace('%', '').replace(',', '')
                sanitized_value = sign + number_part
            else:
                # 没有明确正负号，直接处理
                sanitized_value = sanitized_value.replace('%', '').replace(',', '')
        
        try:
            num_val = float(sanitized_value) if sanitized_value not in [None, '', 'N/A', '-', '+'] else 0
        except (ValueError, TypeError):
            # 如果无法转换为数值，返回无样式
            return (None, None)
        
        # 调试信息：打印解析结果（可在生产环境中移除）
        # print(f"原始值: {value} -> 解析值: {num_val}")
        
        color = None
        text_color_value = None
        
        # === 第二步：根据风格类型计算颜色 ===
        if self.style_type == self.PROFIT_LOSS:
            # 盈亏风格：使用固定颜色，正数红色，负数绿色
            # 注意：这里是非渐变模式，只有两种固定颜色
            # 严格按照数值正负判断：> 0 为红色，< 0 为绿色，= 0 为中性色
            if num_val > 0:
                color = self.COLOR_SCHEMES[self.PROFIT_LOSS]['positive']  # 红色
            elif num_val < 0:
                color = self.COLOR_SCHEMES[self.PROFIT_LOSS]['negative']  # 绿色
            else:
                # num_val == 0，可以选择不着色或使用中性色
                color = None  # 0值不着色
        
        elif self.style_type == self.PERFORMANCE:
            # 性能风格：使用固定颜色，正数绿色，负数红色
            # 注意：这里是非渐变模式，只有两种固定颜色
            if num_val > 0:
                color = self.COLOR_SCHEMES[self.PERFORMANCE]['positive']  # 绿色
            elif num_val < 0:
                color = self.COLOR_SCHEMES[self.PERFORMANCE]['negative']  # 红色
            else:
                # num_val == 0，不着色
                color = None

        elif self.style_type.startswith(self.HEATMAP):
            # 热力图风格：需要计算渐变强度
            # 这里处理所有渐变效果，包括从其他风格转换来的
            intensity = self._calculate_intensity(num_val)
            
            if isinstance(self.custom_colors, list) and len(self.custom_colors) >= 2:
                # 多色渐变：如 ['绿', '白', '红'] 三色渐变
                # 用于盈亏风格和性能风格的渐变模式
                color = self._interpolate_multi_color(self.custom_colors, intensity)
            elif isinstance(self.custom_colors, dict) and 'min' in self.custom_colors and 'max' in self.custom_colors:
                # 双色渐变：如 {'min': '浅绿', 'max': '深绿'}
                # 用于传统热力图
                color = self._interpolate_color(self.custom_colors['min'], self.custom_colors['max'], intensity)
            else:
                # 默认双色渐变：当custom_colors为None或格式不对时的备选方案
                # 使用内置的默认热力图颜色（浅绿到深绿）
                color = self._interpolate_color(self.COLOR_SCHEMES[self.HEATMAP]['min'], self.COLOR_SCHEMES[self.HEATMAP]['max'], intensity)

        elif self.style_type == self.GRADIENT_RAINBOW:
            # 彩虹渐变风格：从紫色到红色的彩虹色谱
            intensity = self._calculate_intensity(num_val)
            color = self._get_rainbow_color(intensity)

        # === 第三步：确定返回的颜色组合 ===
        # 根据配置决定是给背景着色还是给文字着色
        if self.background_color:
            # 背景着色模式：给单元格背景设置颜色
            if self.text_color:
                # 如果同时要求文字着色，根据背景色亮度自动选择对比色
                # 亮背景用黑字，暗背景用白字，确保可读性
                text_color = self._get_contrasting_text_color(color)
            else:
                # 只给背景着色，文字保持默认颜色
                text_color = None
            return (color, text_color)
        else:
            # 文字着色模式：只给文字设置颜色，背景保持透明
            return (None, color if self.text_color else None)
    
    def _calculate_intensity(self, value: float) -> float:
        """
        计算热力图强度
        
        将数值转换为0-1之间的强度值，用于颜色插值计算
        
        Args:
            value: 输入的数值
            
        Returns:
            float: 强度值，范围[0, 1]
        """
        if self.value_range:
            min_val, max_val = self.value_range
            # 检查范围是否有效
            if min_val is not None and max_val is not None and max_val > min_val:
                # 线性映射：将[min_val, max_val]映射到[0, 1]
                # 例如：value=50, range=(-100,100) => intensity=(50-(-100))/(100-(-100))=0.75
                intensity = (value - min_val) / (max_val - min_val)
                # 确保结果在[0, 1]范围内，防止异常值
                return max(0, min(1, intensity))
        
        # 如果没有指定范围，使用简单的对数尺度作为备选方案
        # 这种情况通常不应该发生，但提供一个合理的默认行为
        if value == 0:
            return 0
        # 使用绝对值的简单映射，假设100为一个合理的参考点
        return min(1, abs(value) / 100)  # 简单的强度计算
    
    def _interpolate_color(self, color1_hex: str, color2_hex: str, ratio: float) -> str:
        """
        在两种十六进制颜色之间进行线性插值
        
        例如：color1='#FF0000'(红), color2='#00FF00'(绿), ratio=0.5
        结果：'#808000'(黄绿色，两色的中间色)
        
        Args:
            color1_hex: 起始颜色的十六进制值
            color2_hex: 结束颜色的十六进制值  
            ratio: 插值比例，0.0返回color1，1.0返回color2
            
        Returns:
            str: 插值后的十六进制颜色值
        """
        c1 = QColor(color1_hex)
        c2 = QColor(color2_hex)

        # 对RGB三个分量分别进行线性插值
        r = int(c1.red() + (c2.red() - c1.red()) * ratio)
        g = int(c1.green() + (c2.green() - c1.green()) * ratio)
        b = int(c1.blue() + (c2.blue() - c1.blue()) * ratio)

        # 转换回十六进制格式
        return f"#{r:02x}{g:02x}{b:02x}"
    
    def _interpolate_multi_color(self, colors: list, ratio: float) -> str:
        """
        在多个颜色点之间进行分段线性插值
        
        用于处理多色渐变，如['绿', '白', '红']三色渐变
        将[0,1]的ratio分段映射到不同的颜色区间
        
        例如：colors=['#00FF00', '#FFFFFF', '#FF0000'], ratio=0.25
        0.25在[0, 0.5]区间内，对应绿色到白色的渐变
        
        Args:
            colors: 颜色列表，按顺序排列
            ratio: 插值比例，范围[0, 1]
            
        Returns:
            str: 插值后的十六进制颜色值
        """
        num_segments = len(colors) - 1
        if num_segments <= 0:
            # 如果只有一个颜色或没有颜色，返回第一个或白色
            return colors[0] if colors else "#FFFFFF"

        # 计算每段的长度：将[0,1]平均分成num_segments段
        segment_length = 1.0 / num_segments
        # 确定当前ratio落在哪一段
        segment_index = min(int(ratio / segment_length), num_segments - 1)
        
        # 计算在当前段内的局部比例
        # 例如：ratio=0.25, segment_length=0.5, segment_index=0
        # local_ratio = (0.25 - 0*0.5) / 0.5 = 0.5
        local_ratio = (ratio - segment_index * segment_length) / segment_length
        
        # 获取当前段的起始和结束颜色
        color1 = colors[segment_index]
        color2 = colors[segment_index + 1]

        # 在这两个颜色之间进行插值
        return self._interpolate_color(color1, color2, local_ratio)

    def _get_contrasting_text_color(self, background_color: str) -> str:
        """
        根据背景色亮度返回对比度高的文字颜色
        
        使用相对亮度公式来判断背景色的明暗程度，
        然后选择黑色或白色文字来确保最佳可读性
        
        Args:
            background_color: 背景颜色的十六进制值
            
        Returns:
            str: '#000000'(黑色) 或 '#FFFFFF'(白色)
        """
        if not background_color:
            # 如果没有背景色，默认使用黑色文字
            return '#000000'
        
        # 解析背景色的RGB值
        bg_color = QColor(background_color)
        
        # 计算相对亮度，使用标准的亮度公式
        # 人眼对绿色最敏感，红色次之，蓝色最低
        # 公式来源于ITU-R BT.709标准
        luminance = (0.299 * bg_color.red() + 0.587 * bg_color.green() + 0.114 * bg_color.blue()) / 255
        
        # 根据亮度阈值选择文字颜色
        # 亮度>0.5认为是亮色背景，用黑字；否则用白字
        return '#000000' if luminance > 0.5 else '#FFFFFF'

    def _get_rainbow_color(self, ratio: float) -> str:
        """
        根据比例计算彩虹色
        
        使用预定义的彩虹色谱，从紫色渐变到红色
        模拟自然彩虹的颜色顺序：紫-靛-蓝-绿-黄-橙-红
        
        Args:
            ratio: 比例值，范围[0, 1]
            
        Returns:
            str: 对应的彩虹色十六进制值
        """
        # 使用预定义的彩虹色列表（7种基本彩虹色）
        # 按照光谱顺序排列：紫色 -> 红色
        colors = ['#9400D3', '#4B0082', '#0000FF', '#00FF00', '#FFFF00', '#FF7F00', '#FF0000']
        
        # 边界情况处理
        if ratio <= 0:
            return colors[0]  # 最小值返回紫色
        if ratio >= 1:
            return colors[-1]  # 最大值返回红色
            
        # 使用多色插值来计算中间颜色
        # 将ratio映射到7色彩虹中的具体位置
        num_segments = len(colors) - 1
        segment_length = 1.0 / num_segments
        segment_index = min(int(ratio / segment_length), num_segments - 1)
        
        # 计算在当前颜色段内的局部比例
        local_ratio = (ratio - segment_index * segment_length) / segment_length
        
        # 在相邻两个彩虹色之间插值
        color1 = colors[segment_index]
        color2 = colors[segment_index + 1]

        return self._interpolate_color(color1, color2, local_ratio)


class TgTableStyles:
    """表格样式管理类 - 缓存和复用样式"""
    
    _style_cache = {}
    
    @classmethod
    def get_table_style(cls, font_size, highlight_on_selection, row_height):
        """获取缓存的表格样式"""
        cache_key = f"{font_size}_{highlight_on_selection}_{row_height}"
        
        if cache_key not in cls._style_cache:
            cls._style_cache[cache_key] = cls._generate_style(font_size, highlight_on_selection, row_height)
        
        return cls._style_cache[cache_key]
    
    @classmethod
    def clear_styles(cls):
        """Clear cached table styles (for debug or theme reset)."""
        cls._style_cache.clear()
    
    @classmethod
    def _generate_style(cls, font_size, highlight_on_selection, row_height):
        """生成表格样式字符串"""
        font_size_value = StyleManager.FONT_SIZES.get(font_size, StyleManager.FONT_SIZES['normal'])
        
        # 行高设置
        row_heights = {
            'small': '24px',
            'medium': '32px', 
            'large': '40px'
        }
        row_height_value = row_heights.get(row_height, row_heights['small'])
        
        selection_background_color = StyleManager.COLORS['primary']
        selection_color = 'white'
        if not highlight_on_selection:
            selection_background_color = 'transparent'
            selection_color = 'inherit'

        return f"""
            QTableWidget {{
                border: none;
                border-radius: {StyleManager.BORDER_RADIUS['medium']};
                background-color: {StyleManager.COLORS['background']};
                font-size: {font_size_value};
                selection-background-color: {selection_background_color};
                selection-color: {selection_color};
                gridline-color: transparent;
                alternate-background-color: {StyleManager.COLORS['background_light']};
            }}
            
            QTableWidget::item {{
                height: {row_height_value};
                padding: {StyleManager.SPACING['sm']} {StyleManager.SPACING['md']};
                border-bottom: 1px solid {StyleManager.COLORS['border']};
                border-right: 1px solid {StyleManager.COLORS['border']};
                outline: none;
            }}
            
            QTableWidget::item:selected {{
                background-color: {selection_background_color};
                color: {selection_color};
                outline: none;
            }}
            
            QTableWidget::item:hover {{
                background-color: {StyleManager.COLORS['hover']};
            }}
            
            QHeaderView::section {{
                background-color: {StyleManager.COLORS['surface']};
                color: {StyleManager.COLORS['text_primary']};
                padding: {StyleManager.SPACING['sm']} {StyleManager.SPACING['md']};
                border: 1px solid {StyleManager.COLORS['border']};
                border-bottom: 2px solid {StyleManager.COLORS['primary']};
                font-weight: bold;
                height: {row_height_value};
            }}
            
            QHeaderView::section:hover {{
                background-color: {StyleManager.COLORS['hover']};
            }}
            
            QScrollBar:vertical {{
                border: none;
                background: {StyleManager.COLORS['background_light']};
                width: 12px;
                border-radius: 6px;
            }}
            
            QScrollBar::handle:vertical {{
                background: {StyleManager.COLORS['border']};
                border-radius: 6px;
                min-height: 20px;
            }}
            
            QScrollBar::handle:vertical:hover {{
                background: {StyleManager.COLORS['primary']};
            }}
            
            QScrollBar:horizontal {{
                border: none;
                background: {StyleManager.COLORS['background_light']};
                height: 12px;
                border-radius: 6px;
            }}
            
            QScrollBar::handle:horizontal {{
                background: {StyleManager.COLORS['border']};
                border-radius: 6px;
                min-width: 20px;
            }}
            
            QScrollBar::handle:horizontal:hover {{
                background: {StyleManager.COLORS['primary']};
            }}
        """


class TgTable(QWidget):
    """TG表格组件 - 用于显示表格数据"""
    
    # 信号定义
    cell_clicked = Signal(int, int)  # 行，列
    cell_double_clicked = Signal(int, int)  # 行，列
    selection_changed = Signal()
    
    def __init__(self, headers=None, data=None, parent=None, editable=False, font_size='small', 
                 highlight_on_selection=False, auto_height=False, max_height=None, row_height='small'):
        super().__init__(parent)
        # 初始化TG组件属性
        self._component_name = self.__class__.__name__
        self._is_initialized = False
        self._style_applied = False
        self._theme = 'default'
        self._size_hint = 'medium'
        
        # 设置组件特定属性
        self.editable = editable
        self.font_size = font_size
        self.highlight_on_selection = highlight_on_selection
        self.row_height = row_height
        
        # 列颜色风格管理（支持列索引和列名）
        self.column_styles: Dict[Union[int, str], TgTableColumnStyle] = {}
        
        # 数值列配置，用于排序
        self.numeric_columns: set = set()
        
        # 设置自定义委托
        self.item_delegate = TgTableItemDelegate(self)
        
        # 初始化默认值
        self.headers = []
        self.data = []
        
        # 处理数据输入，支持 DataFrame 或列表数据
        self._process_data_input(headers, data)
        
        # 初始化私有属性，避免 hasattr 检查
        self._auto_height = auto_height
        self._max_height = max_height
        self._height_update_timer = None
        self._last_calculated_height = 0
        
        # 初始化组件
        self.initialize()
        
        # 如果设置了自动高度，在设置完数据后启用
        if auto_height:
            self.set_auto_height(True, max_height=max_height)
    
    @property
    def component_name(self):
        """获取组件名称"""
        return self._component_name
    
    @property
    def is_initialized(self):
        """检查组件是否已初始化"""
        return self._is_initialized
    
    @property
    def style_applied(self):
        """检查样式是否已应用"""
        return self._style_applied
    
    def _process_data_input(self, headers, data):
        """处理数据输入，支持 DataFrame 或列表数据"""
        if isinstance(data, pd.DataFrame):
            # 如果是 DataFrame，提取表头和数据
            self.headers = list(data.columns) if headers is None else headers
            self.data = data.values.tolist()
        else:
            # 传统的列表数据
            # 仅在 headers 不为 None 时更新，避免意外清空
            if headers is not None:
                self.headers = headers
            
            # 仅在 data 不为 None 时更新
            if data is not None:
                self.data = data
    
    def setup_ui(self):
        """设置用户界面"""
        # 创建主布局
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 创建表格部件
        self.table_widget = QTableWidget()
        
        # 应用自定义委托
        self.table_widget.setItemDelegate(self.item_delegate)
        
        # 设置表格数据
        self._setup_table_data()
        
        # 设置表格属性
        self._setup_table_properties()
        
        # 连接信号
        self._connect_signals()
        
        # 添加到布局
        layout.addWidget(self.table_widget)
    
    def _setup_table_data(self):
        """设置表格数据"""
        # 设置行数和列数
        row_count = len(self.data)
        col_count = len(self.headers) if self.headers else 0
        
        self.table_widget.setRowCount(row_count)
        self.table_widget.setColumnCount(col_count)
        
        # 设置表头
        if self.headers:
            self.table_widget.setHorizontalHeaderLabels(self.headers)
        
        # 填充数据并应用列颜色风格
        for row_idx, row_data in enumerate(self.data):
            for col_idx, cell_data in enumerate(row_data):
                if col_idx < col_count:
                    # 使用自定义的TableWidgetItem支持数值排序
                    item = TgTableWidgetItem(
                        text=str(cell_data),
                        numeric_columns=self.numeric_columns,
                        column_index=col_idx
                    )
                    
                    # 应用列颜色风格
                    self._apply_column_style(item, col_idx, cell_data)
                    
                    self.table_widget.setItem(row_idx, col_idx, item)
    
    def _setup_table_properties(self):
        """设置表格属性"""
        # 设置选择模式
        self.table_widget.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
        self.table_widget.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
        
        # 设置编辑触发器
        self.set_editable(self.editable)

        # 设置表头属性
        horizontal_header = self.table_widget.horizontalHeader()
        horizontal_header.setStretchLastSection(True)
        horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        
        # 添加列宽模式跟踪
        self._column_resize_mode = 'stretch'
        self._stretch_last_section = True
        self._auto_resize_content = False
        
        vertical_header = self.table_widget.verticalHeader()
        vertical_header.setVisible(False)
        vertical_header.setSectionResizeMode(QHeaderView.ResizeMode.Fixed)

        # 根据 row_height 设置默认行高
        row_heights_px = {
            'small': 24,
            'medium': 32,
            'large': 40,
        }
        default_row_height = row_heights_px.get(self.row_height, row_heights_px['small'])
        vertical_header.setDefaultSectionSize(default_row_height)
        vertical_header.setMinimumSectionSize(default_row_height)
        
        # 设置网格线
        self.table_widget.setShowGrid(False)
        
        # 设置交替行颜色
        self.table_widget.setAlternatingRowColors(True)
        
        # 设置滚动条策略
        self.table_widget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
        self.table_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
    
    def _connect_signals(self):
        """连接信号"""
        self.table_widget.cellClicked.connect(self._on_cell_clicked)
        self.table_widget.cellDoubleClicked.connect(self._on_cell_double_clicked)
        self.table_widget.itemSelectionChanged.connect(self._on_selection_changed)
    
    def _on_cell_clicked(self, row, column):
        """处理单元格点击事件"""
        self.cell_clicked.emit(row, column)
    
    def _on_cell_double_clicked(self, row, column):
        """处理单元格双击事件"""
        self.cell_double_clicked.emit(row, column)
    
    def _on_selection_changed(self):
        """处理选择变化事件"""
        self.selection_changed.emit()
    
    def apply_style(self):
        """应用样式"""
        style_sheet = TgTableStyles.get_table_style(self.font_size, self.highlight_on_selection, self.row_height)
        self.table_widget.setStyleSheet(style_sheet)
        self._style_applied = True
    
    def initialize(self):
        """初始化组件"""
        if not self._is_initialized:
            self.setup_ui()
            self.apply_style()
            self._is_initialized = True
            self._style_applied = True
    
    def set_data(self, headers, data):
        """设置表格数据"""
        self.headers = headers
        self.data = data
        self._setup_table_data()
        
        # 使用防抖更新高度
        self._schedule_display_update()
    
    def _schedule_display_update(self):
        """延迟显示更新，避免频繁计算"""
        if self._height_update_timer is None:
            self._height_update_timer = QTimer()
            self._height_update_timer.setSingleShot(True)
            self._height_update_timer.timeout.connect(self._update_display)
        
        self._height_update_timer.start(50)  # 50ms延迟
    
    def _update_display(self):
        """更新显示"""
        self._ensure_proper_display()
        
        if self._auto_height:
            self._update_auto_height()
    
    def _ensure_proper_display(self):
        """确保表格正确显示所有内容"""
        # 调整行高以适应内容
        # 当 row_height 为 'auto' 时，根据内容自动调整行高，否则使用固定行高
        if self.row_height in (None, 'auto'):
            self.table_widget.resizeRowsToContents()
        
        # 如果没有启用自动高度，确保表格有足够的空间显示所有行
        if not self._auto_height:
            # 非自动高度模式下，不强制设置最小高度，避免与外部窗口尺寸调整冲突
            # 依赖滚动条显示超出内容
            pass
    
    def batch_update_data(self, updates):
        """批量更新数据，减少重绘次数"""
        self.table_widget.setUpdatesEnabled(False)
        try:
            for update in updates:
                row, col, value = update
                if 0 <= row < len(self.data) and 0 <= col < len(self.data[row]):
                    self.data[row][col] = value
                    item = TgTableWidgetItem(
                        text=str(value),
                        numeric_columns=self.numeric_columns,
                        column_index=col
                    )
                    # 应用列颜色风格
                    self._apply_column_style(item, col, value)
                    self.table_widget.setItem(row, col, item)
        finally:
            self.table_widget.setUpdatesEnabled(True)
            self._schedule_display_update()
    
    def add_row(self, row_data):
        """添加一行数据"""
        self.data.append(row_data)
        row_count = self.table_widget.rowCount()
        self.table_widget.setRowCount(row_count + 1)
        
        for col_idx, cell_data in enumerate(row_data):
            if col_idx < self.table_widget.columnCount():
                item = TgTableWidgetItem(
                    text=str(cell_data),
                    numeric_columns=self.numeric_columns,
                    column_index=col_idx
                )
                # 应用列颜色风格
                self._apply_column_style(item, col_idx, cell_data)
                self.table_widget.setItem(row_count, col_idx, item)
        
        self._schedule_display_update()
    
    def remove_row(self, row_index):
        """删除指定行"""
        if 0 <= row_index < len(self.data):
            self.data.pop(row_index)
            self.table_widget.removeRow(row_index)
            self._schedule_display_update()
    
    def clear_data(self):
        """清空表格数据"""
        self.data.clear()
        self.table_widget.setRowCount(0)
        self._schedule_display_update()
    
    def get_selected_row(self):
        """获取选中的行索引"""
        current_row = self.table_widget.currentRow()
        return current_row if current_row >= 0 else None
    
    def get_selected_data(self):
        """获取选中行的数据"""
        row_index = self.get_selected_row()
        if row_index is not None and row_index < len(self.data):
            return self.data[row_index]
        return None
    
    def get_cell_data(self, row, column):
        """获取指定单元格的数据"""
        if 0 <= row < len(self.data) and 0 <= column < len(self.data[row]):
            return self.data[row][column]
        return None
    
    def set_cell_data(self, row, column, value):
        """设置指定单元格的数据"""
        if 0 <= row < len(self.data) and 0 <= column < len(self.data[row]):
            self.data[row][column] = value
            item = TgTableWidgetItem(
                text=str(value),
                numeric_columns=self.numeric_columns,
                column_index=column
            )
            # 应用列颜色风格
            self._apply_column_style(item, column, value)
            self.table_widget.setItem(row, column, item)
    
    def set_editable(self, editable=True):
        """设置表格是否可编辑"""
        if editable:
            self.table_widget.setEditTriggers(QTableWidget.EditTrigger.DoubleClicked | QTableWidget.EditTrigger.SelectedClicked)
        else:
            self.table_widget.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
    
    def resize_columns_to_contents(self):
        """调整列宽以适应内容"""
        self.table_widget.resizeColumnsToContents()
    
    def resize_rows_to_contents(self):
        """调整行高以适应内容"""
        self.table_widget.resizeRowsToContents()
    
    def set_auto_height(self, auto_height=True, max_height=None):
        """设置表格高度自适应
        
        Args:
            auto_height: 是否启用自动高度
            max_height: 最大高度限制，None表示无限制
        """
        self._auto_height = auto_height
        self._max_height = max_height
        
        if auto_height:
            # 延迟到事件循环空闲时再计算高度，确保行高等尺寸已正确测量
            QTimer.singleShot(0, self._update_auto_height)
        else:
            # 当关闭自动高度时，移除所有高度限制
            self.table_widget.setMaximumHeight(16777215)
            self.table_widget.setMinimumHeight(0)
            self.setMaximumHeight(16777215)
            self.setMinimumHeight(0)
    
    def _update_auto_height(self):
        """更新自动高度"""
        if not self._auto_height:
            return
        
        ideal_height = self._calculate_ideal_height()
        
        if ideal_height == self._last_calculated_height:
            return
        
        self._last_calculated_height = ideal_height
        
        target_height = ideal_height
        if self._max_height is not None:
            # 如果内容高度超过最大限制，则使用最大限制作为目标高度
            if ideal_height > self._max_height:
                target_height = self._max_height
        
        # 设置容器（TgTable自身）的固定高度
        # 布局将自动处理内部 QTableWidget 的大小
        self.setMinimumHeight(target_height)
        self.setMaximumHeight(target_height)
    
    def _calculate_ideal_height(self):
        """计算表格的理想高度"""
        header = self.table_widget.horizontalHeader()
        header_height = header.height() if header.isVisible() else 0
        
        content_height = 0
        for i in range(self.table_widget.rowCount()):
            content_height += self.table_widget.rowHeight(i)
            
        # 添加边框宽度
        frame_width = self.table_widget.frameWidth() * 2
        
        # 添加额外边距
        padding = 5
        
        total_height = header_height + content_height + frame_width + padding
        
        # 确保最小高度
        min_height = header_height + 50
        
        return max(total_height, min_height)
    
    def enable_auto_resize(self, enable=True):
        """启用完全自适应（列宽和行高都自适应）"""
        if enable:
            self.resize_columns_to_contents()
            self.resize_rows_to_contents()
            self.set_auto_height(True)
        else:
            self.set_auto_height(False)
    
    def enable_sorting(self, enable=True):
        """启用表格排序功能"""
        self.table_widget.setSortingEnabled(enable)
    
    def set_column_resize_mode(self, mode='interactive', stretch_last=False, auto_resize_content=True):
        """
        设置列宽调整模式
        
        Args:
            mode: 调整模式
                - 'interactive': 可手动拖拽调整
                - 'stretch': 平均分配宽度
                - 'content': 根据内容自动调整
                - 'fixed': 固定宽度
            stretch_last: 是否拉伸最后一列
            auto_resize_content: 是否初始时根据内容自动调整
        """
        horizontal_header = self.table_widget.horizontalHeader()
        
        # 保存设置用于后续数据更新
        self._column_resize_mode = mode
        self._stretch_last_section = stretch_last
        self._auto_resize_content = auto_resize_content
        
        if mode == 'interactive':
            horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.Interactive)
        elif mode == 'stretch':
            horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        elif mode == 'content':
            horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
        elif mode == 'fixed':
            horizontal_header.setSectionResizeMode(QHeaderView.ResizeMode.Fixed)
        
        horizontal_header.setStretchLastSection(stretch_last)
        
        if auto_resize_content and mode in ['interactive', 'fixed']:
            # 对于可交互和固定模式，初始时根据内容调整
            self.resize_columns_to_contents()
    
    def set_numeric_columns(self, columns: Union[List[int], List[str], List[Union[int, str]]]):
        """
        设置数值列，这些列将按数值排序而不是字符串排序
        
        Args:
            columns: 数值列的索引或列名列表
        """
        self.numeric_columns.clear()
        for col in columns:
            if isinstance(col, str):
                # 列名转换为索引
                try:
                    col_idx = self.headers.index(col)
                    self.numeric_columns.add(col_idx)
                except ValueError:
                    continue
            elif isinstance(col, int):
                # 直接是索引
                if 0 <= col < len(self.headers):
                    self.numeric_columns.add(col)
        
        # 如果已有数据，重新设置数据以应用数值排序
        if hasattr(self, 'data') and self.data:
            self._setup_table_data()
    
    def set_column_width(self, column, width):
        """设置指定列的宽度"""
        if 0 <= column < self.table_widget.columnCount():
            self.table_widget.setColumnWidth(column, width)
    
    def hide_column(self, column):
        """隐藏指定列"""
        if 0 <= column < self.table_widget.columnCount():
            self.table_widget.hideColumn(column)
    
    def update_data(self, headers=None, data=None):
        """更新表格数据，支持 DataFrame 或列表数据"""
        # 如果data为None，则不进行任何操作，避免意外清空
        if data is None:
            return
            
        sorting_enabled = self.table_widget.isSortingEnabled()
        if sorting_enabled:
            header = self.table_widget.horizontalHeader()
            sort_col = header.sortIndicatorSection()
            sort_order = header.sortIndicatorOrder()
            self.table_widget.setSortingEnabled(False)
        self._process_data_input(headers, data)
        self._setup_table_data()
        if sorting_enabled:
            self.table_widget.setSortingEnabled(True)
            self.table_widget.sortItems(sort_col, sort_order)

        # 数据加载完毕后，刷新列颜色风格，确保颜色映射使用最新的数据范围
        self._refresh_column_styles()
        
        # 如果启用了自动调整内容宽度，重新调整列宽
        if hasattr(self, '_auto_resize_content') and self._auto_resize_content:
            if self._column_resize_mode in ['interactive', 'fixed']:
                self.resize_columns_to_contents()
        
        # 如果启用了自动高度，重新计算
        if self._auto_height:
            self._update_auto_height()
    
    def show_column(self, column):
        """显示指定列"""
        if 0 <= column < self.table_widget.columnCount():
            self.table_widget.showColumn(column)
    
    def _get_column_index(self, column_identifier: Union[int, str]) -> Optional[int]:
        """
        获取列索引
        
        Args:
            column_identifier: 列标识符，可以是列索引(int)或列名(str)
            
        Returns:
            列索引，如果找不到返回None
        """
        if isinstance(column_identifier, int):
            # 直接是索引
            if 0 <= column_identifier < len(self.headers):
                return column_identifier
        elif isinstance(column_identifier, str):
            # 根据列名查找索引
            try:
                return self.headers.index(column_identifier)
            except ValueError:
                pass
        return None
    
    def _normalize_column_key(self, column_identifier: Union[int, str]) -> Union[int, str]:
        """
        标准化列标识符，优先使用列名
        
        Args:
            column_identifier: 列标识符
            
        Returns:
            标准化的列标识符（优先返回列名）
        """
        if isinstance(column_identifier, int):
            # 如果是索引，转换为列名
            if 0 <= column_identifier < len(self.headers):
                return self.headers[column_identifier]
        elif isinstance(column_identifier, str):
            # 如果是列名，直接返回
            if column_identifier in self.headers:
                return column_identifier
        return column_identifier
    
    def set_column_style(self, column: Union[int, str], style_type: str, 
                        background_color: bool = True, text_color: bool = False,
                        gradient: bool = False):
        """
        设置列颜色风格
        
        Args:
            column: 列标识符，可以是列索引(int)或列名(str)
            style_type: 风格类型，使用TgTableColumnStyle的常量
            background_color: 是否应用背景色
            text_color: 是否应用文字色
            gradient: 是否启用渐变效果。当为True时自动转换为热力图风格并计算范围
        """
        # 验证列是否存在
        column_index = self._get_column_index(column)
        if column_index is None:
            raise ValueError(f"列 '{column}' 不存在")

        # 根据是否启用渐变选择不同的处理方式
        # gradient=True: 转换为热力图风格，使用自定义颜色和范围实现渐变效果
        # gradient=False: 保持原始风格，使用内置固定颜色
        if gradient:
            # === 渐变模式 ===
            # 所有渐变效果都通过热力图风格实现，因为热力图支持颜色插值
            effective_style_type = TgTableColumnStyle.HEATMAP
            
            if style_type == TgTableColumnStyle.PROFIT_LOSS:
                # 盈亏风格渐变：绿色(负数) -> 白色(0) -> 红色(正数)
                # 符合中国股市习惯：绿跌红涨
                colors_config = ['#1a9850', '#ffffff', '#d73027']  # 绿-白-红
                # 使用对称范围确保0值在颜色中心（白色）
                range_config = self._calculate_symmetric_range(column) or self._calculate_column_range(column_index)
            elif style_type == TgTableColumnStyle.PERFORMANCE:
                # 性能风格渐变：红色(差) -> 白色(中性) -> 绿色(好)
                # 符合国际惯例：红色表示不好，绿色表示好
                colors_config = ['#d73027', '#ffffff', '#1a9850']  # 红-白-绿
                # 使用对称范围确保0值在颜色中心（白色）
                range_config = self._calculate_symmetric_range(column) or self._calculate_column_range(column_index)
            else:
                # 其他风格（如直接使用热力图）：使用默认的绿色热力图
                # 浅绿色(最小值) -> 深绿色(最大值)
                colors_config = {'min': '#E8F5E9', 'max': '#1B5E20'}  # 默认热力图
                # 使用实际数据范围，不需要对称
                range_config = self._calculate_column_range(column_index)
        else:
            # === 固定颜色模式 ===
            # 保持原始风格类型，使用内置的固定颜色方案
            effective_style_type = style_type
            colors_config = None  # 不需要自定义颜色，使用内置COLOR_SCHEMES
            range_config = None   # 固定颜色不需要范围计算
            
            # 特殊情况：非渐变的热力图仍需要范围计算
            # 因为热力图本身就是基于数值强度来显示不同深浅的颜色
            if effective_style_type.startswith(TgTableColumnStyle.HEATMAP):
                range_config = self._calculate_column_range(column_index)

        # 标准化列键（优先使用列名）
        column_key = self._normalize_column_key(column)

        # 创建风格对象，只传入基础参数
        style_obj = TgTableColumnStyle(
            style_type=effective_style_type,    # 最终确定的风格类型（可能从原始类型转换为HEATMAP）
            column_identifier=column,           # 列标识符（索引或名称）
            background_color=background_color,  # 是否给背景着色
            text_color=text_color              # 是否给文字着色
        )
        
        # 只有在需要时才设置颜色配置和范围
        # colors_config: 渐变颜色方案（None表示使用内置固定颜色）
        # range_config: 数值范围，用于计算渐变强度（None表示不需要渐变计算）
        if colors_config is not None or range_config is not None:
            style_obj.set_colors_and_range(colors_config, range_config)
        
        self.column_styles[column_key] = style_obj

        # 重新应用风格到现有数据
        self._refresh_column_styles()
    
    def remove_column_style(self, column: Union[int, str]):
        """移除列颜色风格"""
        column_key = self._normalize_column_key(column)
        if column_key in self.column_styles:
            del self.column_styles[column_key]
            self._refresh_column_styles()
    
    def clear_column_styles(self):
        """清除所有列颜色风格"""
        self.column_styles.clear()
        self._refresh_column_styles()
    
    def set_profit_loss_style(self, column: Union[int, str], 
                              background_color: bool = True, 
                              text_color: bool = False,
                              gradient: bool = False):
        """
        设置盈亏风格：
        当gradient=False时：大于0显示固定红色，小于0显示固定绿色
        当gradient=True时：大于0显示红色渐变，小于0显示绿色渐变，根据值的大小显示渐变强度
        
        Args:
            column: 列标识符，可以是列索引(int)或列名(str)
            background_color: 是否应用背景色
            text_color: 是否应用文字色
            gradient: 是否启用渐变效果
        """
        self.set_column_style(
            column,
            style_type=TgTableColumnStyle.PROFIT_LOSS,
            background_color=background_color,
            text_color=text_color,
            gradient=gradient
        )
    
    def set_performance_style(self, column: Union[int, str], 
                              background_color: bool = True, 
                              text_color: bool = False,
                              gradient: bool = False):
        """
        设置性能风格：
        当gradient=False时：大于0显示固定绿色，小于0显示固定红色
        当gradient=True时：大于0显示绿色渐变，小于0显示红色渐变，根据值的大小显示渐变强度
        
        Args:
            column: 列标识符，可以是列索引(int)或列名(str)
            background_color: 是否应用背景色
            text_color: 是否应用文字色
            gradient: 是否启用渐变效果
        """
        self.set_column_style(
            column,
            style_type=TgTableColumnStyle.PERFORMANCE,
            background_color=background_color,
            text_color=text_color,
            gradient=gradient
        )
    
    def _apply_column_style(self, item: QTableWidgetItem, col_idx: int, cell_data):
        """应用列颜色风格到单元格"""
        # 检查是否有针对该列的样式（支持索引和列名）
        style = None
        
        # 先检查列名
        if col_idx < len(self.headers):
            column_name = self.headers[col_idx]
            if column_name in self.column_styles:
                style = self.column_styles[column_name]
        
        # 如果没找到，再检查索引
        if style is None and col_idx in self.column_styles:
            style = self.column_styles[col_idx]
        
        if style:
            bg_color_str, text_color_str = style.get_style_for_value(cell_data)
            
            # 使用自定义角色存储颜色，由ItemDelegate处理
            if bg_color_str:
                item.setData(BackgroundColorRole, QColor(bg_color_str))
            
            if text_color_str:
                item.setData(TextColorRole, QColor(text_color_str))
    
    def _refresh_column_styles(self):
        """刷新所有列的颜色风格"""
        # ---------------------------  重新计算渐变列的数值范围  ---------------------------
        # 在调用颜色应用逻辑之前，先为所有基于渐变(HEATMAP)的风格重新计算取值范围。
        # 这样可以避免在设置风格时表格尚无数据导致 range=None 的问题，
        # 从而确保后续正负值能够映射到正确的颜色区间。

        for col_key, style in self.column_styles.items():
            # 仅处理渐变/热力图风格
            if style.style_type.startswith(TgTableColumnStyle.HEATMAP):
                col_idx = self._get_column_index(col_key)
                if col_idx is None:
                    continue  # 列不存在

                # 判断是否需要关于 0 对称的范围（盈亏/性能三色渐变：绿-白-红 或 红-白-绿）
                need_symmetric = (
                    isinstance(style.custom_colors, list)
                    and len(style.custom_colors) >= 3
                    and any(c.lower() == "#ffffff" for c in style.custom_colors)
                )

                if need_symmetric:
                    value_range = self._calculate_symmetric_range(col_key) or self._calculate_column_range(col_idx)
                else:
                    value_range = self._calculate_column_range(col_idx)

                # 更新风格对象中的范围配置
                style.set_colors_and_range(style.custom_colors, value_range)

        for row_idx in range(self.table_widget.rowCount()):
            for col_idx in range(self.table_widget.columnCount()):
                item = self.table_widget.item(row_idx, col_idx)
                if item and row_idx < len(self.data) and col_idx < len(self.data[row_idx]):
                    # 清除所有可能的旧样式
                    item.setData(Qt.BackgroundRole, None)
                    item.setData(Qt.ForegroundRole, None)
                    item.setData(BackgroundColorRole, None)
                    item.setData(TextColorRole, None)
                    item.setBackground(QBrush())
                    item.setForeground(QBrush())
                    
                    # 重新应用样式
                    cell_data = self.data[row_idx][col_idx]
                    self._apply_column_style(item, col_idx, cell_data)
    
    def _calculate_column_range(self, column_index: int) -> tuple:
        """计算列的数值范围 (min, max)"""
        min_val, max_val = None, None
        
        for row in range(self.table_widget.rowCount()):
            item = self.table_widget.item(row, column_index)
            if item:
                cell_data = item.text()
                try:
                    # 尝试将文本转换为数值，忽略非数值
                    sanitized_value = cell_data.strip().replace('%', '').replace(',', '')
                    num_val = float(sanitized_value)
                    
                    if min_val is None or num_val < min_val:
                        min_val = num_val
                    if max_val is None or num_val > max_val:
                        max_val = num_val
                except (ValueError, TypeError):
                    continue
        
        return (min_val, max_val)

    def _calculate_symmetric_range(self, column: Union[int, str]) -> Optional[tuple]:
        """
        计算关于0对称的数值范围，用于性能类风格
        
        Args:
            column: 列标识符
            
        Returns:
            tuple: 对称的数值范围 (-max_abs, max_abs)，如果无法计算则返回None
        """
        column_index = self._get_column_index(column)
        if column_index is None:
            return None
            
        min_val, max_val = self._calculate_column_range(column_index)
        
        if min_val is not None and max_val is not None:
            max_abs = max(abs(min_val), abs(max_val))
            if max_abs > 0:
                return (-max_abs, max_abs)
        
        return None 

    def showEvent(self, event):
        """在组件显示后再次更新自动高度，确保布局完成"""
        super().showEvent(event)
        if self._auto_height:
            # 等待一帧后更新，以便行高已经正确计算
            QTimer.singleShot(0, self._update_auto_height) 
